# ~*~ coding: utf-8 ~*~
from flask import request, views, render_template, jsonify, current_app
from flask_login import current_user

from apps.common.utils import fill_date_to_timestamp, timestamp_obj_to_str, list2dict, get_remote_address
from apps.common.mixin import CompatibleWithSelect2Mixin
from apps.common import response
from apps.extensions import db
from apps.perms.models import UserRole, Role
from apps.perms.mixin import PermissionRequiredMixin, LoginRequiredMixin
from ..models import User, PasswordChangeLog, UserLoginLog
from ..forms import UserCreateUpdateForm


__all__ = [
    'UserListAPI', 'UserListView', 'UserCreateView',
    'UserUpdateView', 'UserDeleteView', 'UserPasswordResetView',
    'UserLoginLogAPI', 'UserLoginLogView',
    'UsePasswordChangeLogAPI', 'UserPasswordChangeLogView'
]


class UserListAPI(CompatibleWithSelect2Mixin, PermissionRequiredMixin, LoginRequiredMixin, views.MethodView):
    permission_required = 'users-user-list'

    def get(self):
        create_time = request.args.get('create_time')
        start_time = end_time = None
        if create_time:
            start_time = fill_date_to_timestamp('start', create_time.split('~')[0])
            end_time = fill_date_to_timestamp('end', create_time.split('~')[1])

        filter_conditions = {
            'and': [
                ('is_dimission', 'eq', request.args.get('is_dimission') or None),
                ('create_time', 'gte', start_time),
                ('create_time', 'lte', end_time),
            ],
            'or': [
                ('name', 'like', request.args.get('search')),
                ('username', 'like', request.args.get('search')),
            ],
        }

        # 排序
        sort = request.args.get('sort') or 'create_time'
        order = request.args.get('order') or 'desc'
        order_by_list = [(sort, order), ]

        # 分页
        offset = request.args.get('offset') or 0
        limit = request.args.get('limit') or 12

        # 根据权限查询过滤
        queryset = None
        if request.args.get('role'):
            role_id = request.args.get('role')
            user_ids = db.session.query(UserRole.user_id).filter(UserRole.role_id == role_id)
            user_ids = list(zip(*user_ids))[0]
            queryset = db.session.query(User).filter(User.id.in_(user_ids))

        # Compatible with select2
        if request.values.get('type') == 'select2':
            filter_conditions, order_by_list, offset, limit = self.select2_queryset()

        users, count = User.get_queryset(queryset=queryset,
                                         filter_conditions=filter_conditions,
                                         order_by_list=order_by_list,
                                         offset=offset, limit=limit)
        # 查询用户对应的角色
        user_ids = [user.id for user in users]
        user_roles = db.session.query(UserRole.user_id, Role.name.label('role_name')).\
            join(Role, UserRole.role_id == Role.id). \
            filter(UserRole.user_id.in_(user_ids)).\
            all()
        user_role_dict = list2dict(user_roles)

        results = list()
        for user in users:
            results.append({
                'id': user.id,
                'name': user.name,
                'username': user.username,
                'email': user.email,
                'phone': user.phone,
                'is_dimission': user.is_dimission,
                'is_admin': user.is_admin,
                'otp_level': user.otp_level,
                'roles': user_role_dict.get(user.id)if user_role_dict.get(user.id) else '',
                'last_ip': user.last_ip,
                'last_login': timestamp_obj_to_str(user.last_login),
                'create_time': timestamp_obj_to_str(user.create_time),
            })

        resp = {
            "data": {
                "rows": results,
                "total": count
            }
        }
        return jsonify(resp)


class UserListView(PermissionRequiredMixin, LoginRequiredMixin, views.MethodView):
    permission_required = 'users-user-list'

    def get(self):
        content = {
        }
        return render_template('users/user/user_list.html', **content)


class UserUpdateView(PermissionRequiredMixin, LoginRequiredMixin, views.MethodView):
    permission_required = 'users-user-update'

    def get(self, pk):
        instance = db.session.query(User).get(str(pk))
        roles_ids = db.session.query(UserRole.role_id).filter(UserRole.user_id == instance.id).all()
        if roles_ids:
            roles_ids = ','.join(list(zip(*roles_ids))[0])
        else:
            roles_ids = ''

        context = {
            'id': instance.id,
            'name': instance.name,
            'username': instance.username,
            'email': instance.email,
            'phone': instance.phone,
            'is_admin': instance.is_admin,
            'otp_level': instance.otp_level,
            'role_ids': roles_ids
        }
        return render_template('users/user/user_update.html', **context)

    def post(self, pk):
        instance = db.session.query(User).get(str(pk))
        form = UserCreateUpdateForm(request.values)
        if form.validate_on_submit():
            _clean_data = form.data
            if _clean_data.get('phone') != instance.phone:
                if db.session.query(User).filter_by(phone=_clean_data.get('phone')).count():
                    return response.params_error({
                        'field': 'phone',
                        'message': '手机号不能重复'
                    })

            if _clean_data.get('username') != instance.username:
                if db.session.query(User).filter_by(username=_clean_data.get('username')).count():
                    return response.params_error({
                        'field': 'username',
                        'message': '员工工号不能重复'
                    })

            # otp设置
            if _clean_data.get('otp_level') == 0:
                _clean_data['otp_secret_key'] = None
            if _clean_data.get('otp_level') == 1:
                return response.params_error({
                    'field': 'otp_level',
                    'message': '只能强制开启MFA'
                })

            roles = _clean_data.pop('roles').split(',')

            user = instance.update(**_clean_data, commit=False)
            db.session.flush()

            db.session.query(UserRole).filter(UserRole.user_id == user.id).delete()
            db.session.bulk_insert_mappings(
                UserRole,
                [dict(user_id=user.id, role_id=rid) for rid in roles]
            )
            db.session.commit()
            return response.success()
        else:
            return response.params_error(form.get_error())


class UserCreateView(PermissionRequiredMixin, LoginRequiredMixin, views.MethodView):
    permission_required = 'users-user-create'

    def get(self):
        return render_template('users/user/user_create.html')

    def post(self):
        form = UserCreateUpdateForm(request.values)
        if form.validate_on_submit():
            _clean_data = form.data
            if db.session.query(User).filter_by(phone=_clean_data.get('phone')).count():
                return response.params_error({
                    'field': 'phone',
                    'message': '手机号不能重复'
                })

            if db.session.query(User).filter_by(username=_clean_data.get('username')).count():
                return response.params_error({
                    'field': 'username',
                    'message': '员工工号不能重复'
                })

            # otp设置
            if _clean_data.get('otp_level') == 0:
                _clean_data['otp_secret_key'] = None
            if _clean_data.get('otp_level') == 1:
                return response.params_error({
                    'field': 'otp_level',
                    'message': '只能强制开启MFA'
                })

            roles = _clean_data.pop('roles').split(',')

            user = User.create(**_clean_data, commit=False)
            db.session.flush()

            db.session.bulk_insert_mappings(
                UserRole,
                [dict(user_id=user.id, role_id=rid) for rid in roles]
            )
            db.session.commit()
            return response.success()
        else:
            return response.params_error(form.get_error())


class UserDeleteView(PermissionRequiredMixin, LoginRequiredMixin, views.MethodView):
    permission_required = 'users-user-delete'

    def post(self, pk):
        instance = db.session.query(User).get(pk)
        instance.delete()
        return response.success()


class UserPasswordResetView(PermissionRequiredMixin, LoginRequiredMixin, views.MethodView):
    permission_required = 'users-user-password'

    def post(self, pk):
        # change password
        instance = db.session.query(User).get(pk)
        instance.set_password(User.DEFAULT_PASSWORD)
        instance.update(commit=False)
        # log recode
        log = {
            "user": "{} ({})".format(instance.name, instance.username),
            'ip': get_remote_address(),
            'change_by': "{} ({})".format(current_user.name, current_user.username),
        }
        PasswordChangeLog.create(commit=False, **log)
        # commit
        db.session.commit()
        return response.success()


class UserLoginLogAPI(PermissionRequiredMixin, LoginRequiredMixin, views.MethodView):
    permission_required = 'users-log-login'

    def get(self):
        create_time = request.args.get('create_time')
        start_time = end_time = None
        if create_time:
            start_time = fill_date_to_timestamp('start', create_time.split('~')[0])
            end_time = fill_date_to_timestamp('end', create_time.split('~')[1])

        filter_conditions = {
            'and': [
                ('create_time', 'gte', start_time),
                ('create_time', 'lte', end_time),
            ],
            'or': [
                ('name', 'like', request.args.get('search')),
                ('username', 'like', request.args.get('search')),
            ],
        }

        # 排序
        sort = request.args.get('sort') or 'create_time'
        order = request.args.get('order') or 'desc'
        order_by_list = [(sort, order), ]

        # 分页
        offset = request.args.get('offset') or 0
        limit = request.args.get('limit') or 12

        logs, count = UserLoginLog.get_queryset(queryset=None,
                                                filter_conditions=filter_conditions,
                                                order_by_list=order_by_list,
                                                offset=offset, limit=limit)

        results = list()
        for log in logs:
            results.append({
                'id': log.id,
                'user': log.user,
                'ip': log.ip,
                'city': log.city,
                'mfa': dict(UserLoginLog.MFA_CHOICE).get(log.mfa),
                'reason': dict(UserLoginLog.REASON_CHOICE).get(log.reason),
                'status': log.status,
                'create_time': timestamp_obj_to_str(log.create_time),
            })

        resp = {
            "data": {
                "rows": results,
                "total": count
            }
        }
        return jsonify(resp)


class UserLoginLogView(PermissionRequiredMixin, LoginRequiredMixin, views.MethodView):
    permission_required = 'users-log-login'

    def get(self):
        content = {
        }
        return render_template('users/user/user_login_log.html', **content)


class UsePasswordChangeLogAPI(PermissionRequiredMixin, LoginRequiredMixin, views.MethodView):
    permission_required = 'users-log-password'

    def get(self):
        create_time = request.args.get('create_time')
        start_time = end_time = None
        if create_time:
            start_time = fill_date_to_timestamp('start', create_time.split('~')[0])
            end_time = fill_date_to_timestamp('end', create_time.split('~')[1])

        filter_conditions = {
            'and': [
                ('create_time', 'gte', start_time),
                ('create_time', 'lte', end_time),
            ],
            'or': [
                ('name', 'like', request.args.get('search')),
                ('username', 'like', request.args.get('search')),
            ],
        }

        # 排序
        sort = request.args.get('sort') or 'create_time'
        order = request.args.get('order') or 'desc'
        order_by_list = [(sort, order), ]

        # 分页
        offset = request.args.get('offset') or 0
        limit = request.args.get('limit') or 12

        logs, count = PasswordChangeLog.get_queryset(queryset=None,
                                                     filter_conditions=filter_conditions,
                                                     order_by_list=order_by_list,
                                                     offset=offset, limit=limit)

        results = list()
        for log in logs:
            results.append({
                'id': log.id,
                'user': log.user,
                'ip': log.ip,
                'change_by': log.change_by,
                'create_time': timestamp_obj_to_str(log.create_time),
            })

        resp = {
            "data": {
                "rows": results,
                "total": count
            }
        }
        return jsonify(resp)


class UserPasswordChangeLogView(PermissionRequiredMixin, LoginRequiredMixin, views.MethodView):
    permission_required = 'users-log-password'

    def get(self):
        content = {
        }
        return render_template('users/user/user_passwordchange_log.html', **content)
